package fybug.nulll.pdstream.WR.Stream;
import com.sun.istack.internal.NotNull;
import com.sun.istack.internal.Nullable;

import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.Reader;
import java.nio.CharBuffer;

import fybug.nulll.pdfunctionlibrary.Annotations.MaybeStop;
import fybug.nulll.pdfunctionlibrary.Processing.Err.Stop;
import fybug.nulll.pdfunctionlibrary.Util.Processing.Arrarys;
import fybug.nulll.pdstream.Source.StreamSource;
import fybug.nulll.pdstream.StreamOperator;
import fybug.nulll.pdstream.WR.Err.CharStreamErr;
import fybug.nulll.pdstream.WR.Stream.Interface.ReadCheck;
import fybug.nulll.pdstream.WR.Stream.Interface.StringCheck;
import fybug.nulll.pdstream.WR.W_R;

import static fybug.nulll.pdstream.WR.Stream.Buffer.upBufferSize;
/**
 * <h2>字符流读取器.</h2>
 * <pre>
 * <b>建议使用工场类 {@link W_R} 构造</b>
 *
 * 从绑定的 <b>操作源</b> 中获取的被操作流进行读取
 * 支持 <b>批量读取</b>
 * 可使用该操作器中的方法对操作源进行 <b>流的重绑定</b>
 * 由于 <b>字符流的读取方式</b> 所有操作均为 <b>连续操作</b> ,可有效解决不稳定流的读取问题
 *
 * 但不建议使用该类中的方法 <b>读取大量数据</b>
 * 会 <b>内存不足</b> 的</pre>
 * <pre>常规操作方法：
 * {@link #read()}
 * </pre>
 *
 * @author fybug
 * @version 0.1.9
 * @see StreamOperator
 * @see Reader
 * @see W_R
 * @see CharStreamErr
 * @since JDK 1.8
 */
@SuppressWarnings( "all" )
public final
class Read extends StreamOperator<Reader, Object> {
    /** <p>构造一个读取器并绑定文本源.</p> */
    public
    Read(@Nullable final StreamSource<Reader, Object> r) { binSource(r); }

    /*
     * MainSource
     */

    /** <p>绑定要读取的文本源.</p> */
    @NotNull
    public final
    Read binSource(@Nullable final StreamSource<Reader, Object> r)
    {
        super.binSource(r);
        return this;
    }

    /** <p>重绑定操作源中的流.</p> */
    @NotNull
    public final
    Read binReader(@Nullable final Reader... readers) {
        try {
            source().binStream(readers);
        } catch ( Stop ignored ) {
        }
        return this;
    }

    /** <p>增加操作源中的流.</p> */
    @NotNull
    public final
    Read appendReader(final Reader... readers) {
        try {
            @NotNull StreamSource<Reader, Object> source = source();
            Reader[] re;
            try {
                re = source.stream();
            } catch ( Stop e ) {
                re = null;
            }
            source.binStream(Arrarys.append(re, readers));
        } catch ( Stop ignored ) {
        }
        return this;
    }

    /*
     * Read
     */

    /**
     * <p>读取绑定的流的字符串.</p>
     * <pre>
     * <b>按顺序</b> 将流中的字符逐个读出,并存放在 {@link StringBuilder}
     * 适用于所有流
     * </pre>
     *
     * @throws CharStreamErr 读取过程发生错误
     */
    @NotNull
    public final
    String read() throws CharStreamErr {
        @NotNull Reader[] r;
        // 检查
        try {
            r = source().stream(); // 读取流组
        } catch ( Stop ignored ) {
            return "";
        }
        @NotNull //* 使用字符缓冲区,降低修改开销 *//
                StringBuilder stringBuffer = new StringBuilder(); // 读取缓存
        @NotNull Reader re = null; // 减少寻址次数
        int i; // 单次读取返回
        try {
            synchronized ( r ){
                for ( int i1 = 0; i1 < r.length; i1++ ){
                    /* 读取所有流 */
                    re = r[i1];
                    while( (i = re.read()) != -1 )
                        // 读取一个字符并放入缓存区
                        stringBuffer.append((char) i);
                }
            }
            i = 0;
            re = null;
        } catch ( IOException e ) {
            throw new CharStreamErr(e.getMessage(), re, null);
        }
        return stringBuffer.toString();
    }

    /**
     * <p>使用连续操作从绑定的流中进行读取.</p>
     * <pre>
     * 将绑定的流中的字符 <b>按顺序</b> 进行读取
     * 每次读取一个片段
     * 可使用 <b>中介接口</b> 对读取的片段进行处理
     * 适用于所有流
     * </pre>
     *
     * @param size  片段大小
     * @param check 中介接口
     *
     * @throws CharStreamErr 读取出错
     * @see StringCheck
     */
    @NotNull
    public final
    String sustained(final int size, @Nullable final StringCheck check) throws CharStreamErr
    {
        return monitor(size, check == null ? null : new ReadCheck() {
            @NotNull
            @Override
            @MaybeStop
            public
            String check(@Nullable final String s) throws Stop { return check.check(s); }

            @Override
            @MaybeStop
            public
            void init() { check.init(); }

            @Override
            public
            void destroy() { check.destroy(); }
        });
    }

    /**
     * <p>使用连续操作从绑定的流中进行读取.</p>
     * <pre>
     * 将绑定的流中的字符 <b>按顺序</b> 进行读取
     * 每次读取一个片段
     * 可以使用接口 <b>处理读取的数据并动态调整读取片段</b> 大小
     * 在读取过程中设置片段大小为{@code 0} 可以中断读取
     * 适用于所有流
     * </pre>
     *
     * @param size 初始片段大小
     * @param see  监听接口
     *
     * @throws CharStreamErr 读取过程发生错误
     * @see ReadCheck
     */
    @NotNull
    public final
    String monitor(final int size, @Nullable final ReadCheck see) throws CharStreamErr {
        @NotNull Reader[] r;
        // 检查
        try {
            r = source().stream(); // 读取流组
            if (size <= 0)
                return "";
        } catch ( Stop e ) {
            return "";
        }
        @NotNull //* 使用缓冲输出流 *//
                CharArrayWriter writer = new CharArrayWriter(); // 暂存区
        @NotNull //* 使用 Nio 包的缓冲区,提高缓冲区复用效率 *//
                CharBuffer charBuffer = CharBuffer.allocate(size); // 缓冲区
        @NotNull String bufferText;
        @Nullable Reader re = null;
        int readSize; // 读取数量
        try {
            synchronized ( r ){
                if (see != null)
                    see.init();
                for ( int i = 0; i < r.length; i++ ){
                    /* 读取所有流 */
                    re = r[i]; // 减少寻址
                    while( (readSize = re.read(charBuffer)) != -1 ){
                        /* 读取到缓冲区并检查数量 */
                        bufferText = String.valueOf(charBuffer.array(), 0, readSize);
                        // 使用检查接口
                        bufferText = see != null ? see.check(bufferText) : bufferText;
                        writer.write(bufferText == null ? "" : bufferText);
                        bufferText = null;
                        if (readSize < charBuffer.capacity())
                            /* 读取完成 */
                            break;
                        charBuffer = upBufferSize(see, charBuffer, readSize);
                    }
                }
            }
            charBuffer = null;
            re = null;
        } catch ( Stop ignored ) {
        } catch ( IOException e ) {
            // 读取出错
            throw new CharStreamErr(e.getMessage(), re, null);
        } finally {
            if (see != null)
                see.destroy();
        }
        return writer.toString();
    }
}